/*
 * Licensed under the GNU General Public License version 2 with exceptions. See
 * LICENSE file in the project root for full license information
 */
#include <esc_foe.h>
#include <string.h>
#include "main.h"
#include "bootstrap.h"
#include "flash_if.h"

/* Dummy values pointing to a flash writing area when updating Firmware */
#define FLASH_BLOCK_SIZE 128 * 1024
#define FLASH_WRITE_BLOCK_SIZE 500

#define POWER_ON_COUNT VirtAddVarTab[0]
#define FACTORY_CRC VirtAddVarTab[1]
#define DATABANK1_CRC VirtAddVarTab[2]
#define DATABANK2_CRC VirtAddVarTab[3]
#define FACTORY_ADDR_LWORD VirtAddVarTab[4]
#define FACTORY_ADDR_HWORD VirtAddVarTab[5]
#define APP_ADDR_LWORD VirtAddVarTab[6]
#define APP_ADDR_HWORD VirtAddVarTab[7]
#define DOWN_ADDR_LWORD VirtAddVarTab[8]
#define DOWN_ADDR_HWORD VirtAddVarTab[9]
#define PARAINITFLAG VirtAddVarTab[NB_OF_VAR - 1]

uint32_t flash_foe_buffer(foe_file_cfg_t *self, uint8_t *data, size_t length);

uint32_t local_boot_state = BOOT_IDLE;
static uint32_t boot_started = 0;
static uint32_t boot_watch_dog = 0;
static uint32_t current_tick_ms;
static foe_file_cfg_t files[] = {{
    .name = "project",
    .max_data = FLASH_BLOCK_SIZE,
    .dest_start_address = 0,
    .address_offset = 0,
    .filepass = 0,                     // 0x6533,/*password ctb tel 6907[6533]*/
    .write_function = flash_foe_buffer /* NULL if not used */
}};
static uint8_t fbuf[FLASH_WRITE_BLOCK_SIZE];
static foe_cfg_t config = {.buffer_size = FLASH_WRITE_BLOCK_SIZE, /* Buffer size before we flush to destination */
                           .fbuffer = (uint8_t *)&fbuf,
                           .n_files = sizeof(files) / sizeof(files[0]),
                           .files = files};


uint32_t tick_to_ms(uint32_t tick)
{
    return tick;
}
uint32_t tick_get(void)
{
    return HAL_GetTick();
}
void boot_soft_reset(void)
{
    DPRINT("boot_soft_reset\n");
    HAL_Delay(100);
    HAL_NVIC_SystemReset();
}

void init_boothook(void)
{
    DPRINT("osal_init_boothook\n");
    FOE_config(&config);
    local_boot_state = BOOT_START;
}

void boot_inithook(void)
{
    if (local_boot_state == BOOT_FAILED)
    {
        DPRINT("osal_boot_inithook : BOOT failed\n");
        local_boot_state = BOOT_IDLE;
    }
    else
    {
        DPRINT("osal_boot_inithook\n");
        local_boot_state = BOOT_SWAP;
    }
}
int32_t boot_swap_flash(void)
{
    int32_t ret=0;
    return ret;
}
uint32_t flash_foe_buffer(foe_file_cfg_t *self, uint8_t *data, size_t length)
{
    uint32_t flash_cmd_failed = 0;
    uint32_t calculated_address;
    static uint8_t s_flag = 0;
    if (0 == strncmp(self->name, files[0].name, strlen(files[0].name)))
    {
        if (0 == self->address_offset)
        {
            self->dest_start_address = btdata.DownloadAddress;
            if (0x08040000 == self->dest_start_address)
            {
                FLASH_If_Erase(self->dest_start_address);
                DPRINT("Start falsh address 0x%08X\n", self->dest_start_address);
                s_flag = 1;
            }
            else
            {
                DPRINT("Wrong start falsh address 0x%08X\n", self->dest_start_address);
                s_flag = 0;
            }
        }
        calculated_address = self->dest_start_address + self->address_offset;
        if (s_flag)
        {
            flash_cmd_failed = FLASH_If_Write(calculated_address, (uint32_t *)data, (length+3) >> 2);
            if (flash_cmd_failed)
            {
                DPRINT("flash_program_failed %x\n", calculated_address);
            }
            if(FLASH_WRITE_BLOCK_SIZE > length)
            {
            }
        }
    }
    return flash_cmd_failed;
}

void bootstrap_state(void)
{
    switch (local_boot_state)
    {
    /* BOOT_START is set from Init->Boot hook called from ESC_state */
    case BOOT_START: {
        local_boot_state = BOOT_UPDATING;
        boot_started = tick_to_ms(tick_get());
        boot_watch_dog = boot_started + BOOT_WATCHDOG_MS;
        DPRINT("boot_started: %d, boot_watch_dog: %d\n", boot_started, boot_watch_dog);
        break;
    }
    case BOOT_UPDATING: {
        current_tick_ms = tick_to_ms(tick_get());
        if (boot_watch_dog < current_tick_ms)
        {
            local_boot_state = BOOT_FAILED;
            DPRINT("boot failed at: %d\n", current_tick_ms);
        }
        break;
    }
    case BOOT_SWAP: {
        /* BOOT_SWAP is set from Boot->Init hook called from ESC_state */
        if (0 == boot_swap_flash())
        {
            /* IF it went OK we'll trigger BOOT_RESET */
            local_boot_state = BOOT_RESET;
        }
        else
        {
            DPRINT("boot_swap_failed\n");
            local_boot_state = BOOT_FAILED;
        }
        break;
    }
    case BOOT_RESET: {
        /* BOOT_RESET is set from Boot->Init hook called from ESC_state */
        local_boot_state = BOOT_IDLE;
        boot_soft_reset();
        break;
    }
    case BOOT_FAILED:
    default:
        break;
    }
}

void ShowProjectInfomation(bootloader_t*p)
{
    project_info_t*pi;
    pi = p->bootinfo;
    DPRINT("Project Name:%s\n",pi->name);
    DPRINT("Version:%d.%d.%d[%06X]\n",pi->ver_major,pi->ver_minor,pi->ver_revision,pi->swver);
    DPRINT("Build:%s %s\n\n",pi->build_date,pi->build_time);
    pi = p->appinfo;
    DPRINT("ApplicationAddress:0x%08X\n",p->ApplicationAddress);
    if(0xFFFFFFFF != pi->swver)
    {
        DPRINT("Project Name:%s\n",pi->name);
        DPRINT("Version:%d.%d.%d[%06X]\n",pi->ver_major,pi->ver_minor,pi->ver_revision,pi->swver);
        DPRINT("Build:%s %s\n\n",pi->build_date,pi->build_time);
    }
    pi = p->downinfo;
    DPRINT("DownloadAddress:0x%08X\n",p->DownloadAddress);
    if(0xFFFFFFFF != pi->swver)
    {
        DPRINT("Project Name:%s\n",pi->name);
        DPRINT("Version:%d.%d.%d[%06X]\n",pi->ver_major,pi->ver_minor,pi->ver_revision,pi->swver);
        DPRINT("Build:%s %s\n",pi->build_date,pi->build_time);
    }
    DPRINT("PowerOnCount:%u\n\n",p->PowerOnCount);
}
